//@author A0086393U
package rightnow.ui.widgets;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import rightnow.logic.FeedbackProvider;
import rightnow.storage.Task;
import rightnow.ui.MainApp;
import rightnow.util.UiOptions;
import rightnow.util.UiUtils;

/**
 * FX-controller for the ListView.fxml display element. Contains logic
 * managing how to display the list being shown. ListController is an
 * Observer, so it can update itself when notified without being passed
 * around the program.
 *
 */
public class ListController extends AnchorPane implements Observer {
	private static final Logger LOG = LoggerFactory.getLogger(MainApp.class);

	private static final String LIST_VIEW_FXML = "/fxml/ListView.fxml";
	private static final String PAGE_COUNT_TEXT = "Page %d of %d";
	private static final String highlightColor = "rgba(220, 204, 255, 0.4)";
	private static final int OFFSET = 1;

	/* These are components in this section of the UI, and they will be
	 * injected into by FXML automatically.
	 * 
	 * DO NOT CREATE NEW INSTANCES OF THESE! (unless you know what you are doing)
	 */
	@FXML private VBox listVBox;
	@FXML private Label pageCountLabel;
	@FXML private Label listNameLabel;
	
	private URL listViewFxmlUrl = getClass().getResource(LIST_VIEW_FXML);
	private ArrayList<AnchorPane> tasksInPane;
	private List<Task> tasksBeingListed;
	private TaskController emptyPane = new TaskController();
	private int indexToHighlight = -1;

	
	@FXML
	private void initialize() {
		LOG.info("Creating list controller");
		
		assert listVBox != null : "fx:id=\"listVBox\" was not injected";
		LOG.debug("Fxml dependencies successfully injected");
		
		LOG.info("List controller successfully created");
	}
	
	public ListController() {
		super();
		
		// Load fxml because ListPresenter is a custom component
		UiUtils.loadFxml(this, this, listViewFxmlUrl);
				
		// Use an empty task list to initialize the list view
		ArrayList<Task> emptyTaskList = new ArrayList<Task>();
		listVBox.getChildren().setAll(createTaskViews(emptyTaskList));
		
		// Register to observable
		FeedbackProvider.getInstance().addObserver(this);
	}
	
	private ArrayList<AnchorPane> createTaskViews(List<Task> tasksToList) {
		LOG.info("Creating a new list of tasks to display");
		tasksInPane = new ArrayList<AnchorPane>(UiOptions.TASKS_PER_PAGE);
		tasksBeingListed = tasksToList;
		
		// if tasksToList is empty, display the emptyPane
		if (tasksToList.isEmpty()) {
			LOG.info("List of tasks to display is empty");
			tasksInPane.add(emptyPane);
			return tasksInPane;
		}
				
		// Create a pane for each available task and add it to the tasksInPane.
		// Keep track of the index for UI purposes, as the index will serve as a
		// way to specify which tasks to operate upon.
		int index = 0;
		boolean separate = false;
		for (Task t : tasksToList) {
			AnchorPane taskPane;
			if (index == indexToHighlight) {
				// highlight the task and default to detailed view
				taskPane = new TaskController(t, makeDisplayIndex(index), separate);
				taskPane.setStyle("-fx-background-color: " + highlightColor + ";");
			} else {
				taskPane = new SimpleTaskController(t, makeDisplayIndex(index),
						separate);
			}
			tasksInPane.add(taskPane);
			index++;
			separate = true;
		}
		
		LOG.info("List of tasks to display successfully created");
		
		return tasksInPane;
	}

	private void updateTaskView(List<Task> tasks) {
		listVBox.getChildren().setAll(createTaskViews(tasks));
	}
	
	/**
	 * Toggles the task view of the task at displayIndex from simple to detailed
	 * and vice versa.
	 * @param displayIndex Display index of the task to toggle.
	 * @return True if displayIndex is a valid index in the displayed list.
	 */
	public boolean toggleTaskDetails(int displayIndex) {
		int index = displayIndex - OFFSET;
		
		assert index >= 0 && index < UiOptions.TASKS_PER_PAGE : 
			"Index to toggle is negative or larger than the maximum possible!";
		
		// If index is greater than number of tasks being listed, is a valid index
		// but is an index that is not present on the page
		if (index >= tasksBeingListed.size()) {
			return false;
		}
		
		boolean showSeparator = true;
		if (index == 0) {
			showSeparator = false;
		}
		
		AnchorPane newTaskPane;
		
		// If the index shown uses a SimpleTaskController, then reconstruct it
		// as a TaskController and vice versa
		if (listVBox.getChildren().get(index) instanceof SimpleTaskController) {
			newTaskPane = new TaskController(tasksBeingListed.get(index),
					displayIndex, showSeparator);
			listVBox.getChildren().set(index, newTaskPane);
		} else if (listVBox.getChildren().get(index) instanceof TaskController) {
			newTaskPane = new SimpleTaskController(
					tasksBeingListed.get(index), displayIndex, showSeparator);
		} else {
			LOG.warn("Task being toggled is neither a SimpleTaskController or "
					+ "TaskController! Check what you are putting into the UI List!");
			throw new Error("Attempt to toggle a task that is neither a "
					+ "SimpleTaskController or TaskController!");
		}
		
		// newTaskPane should have been initialized and created
		assert newTaskPane != null : "newTaskPane not created!";
		
		listVBox.getChildren().set(index, newTaskPane);

		return true;
	}
	
	/**
	 * Converts the actual index in the list to the index displayed on screen.
	 * @param realIndex Actual index of the task in the list.
	 * @return The displayed index of the task at realIndex in the list.
	 */
	private int makeDisplayIndex(int realIndex) {		
		return realIndex + OFFSET;
	}
	
	/* Getters and setters */
	
	public VBox getListVBox() {
		return listVBox;
	}

	public void setListVBox(VBox listVBox) {
		this.listVBox = listVBox;
	}
	
	public void setPageCount(int current, int total) {
		pageCountLabel.setText(String.format(PAGE_COUNT_TEXT, current, total));
	}
	
	public void setListName(String name) {
		listNameLabel.setText(name);
	}
	
	/* Observer */
	
	@Override
	public void update(Observable obs, Object arg) {
		if (obs == FeedbackProvider.getInstance()) {
			FeedbackProvider fp = FeedbackProvider.getInstance();
			if (fp.hasListChanged()) {
				indexToHighlight = fp.getChangedTaskIndex();
				updateTaskView(fp.getTasksForUi());
				setPageCount(fp.getCurrentPage(), fp.getTotalPages());
				setListName(fp.getListName());
			}
		}
	}
}
